package org.webscale.util.sdb;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.log4j.Logger;
import org.webscale.ApplicationException;

import com.xerox.amazonws.sdb.Item;
import com.xerox.amazonws.sdb.ItemAttribute;
import com.xerox.amazonws.sdb.SDBException;

public class DefaultMapStrategy implements MappingStrategy {
	private static final Logger log = Logger
			.getLogger(DefaultMapStrategy.class);

	private String environment;

	public List<ItemAttribute> getItemAttributes(Object obj) {
		List<ItemAttribute> attrs = new ArrayList<ItemAttribute>();
		try {
			Map map = BeanUtils.describe(obj);
			Iterator keys = map.keySet().iterator();
			while (keys.hasNext()) {
				String key = keys.next().toString();
				log.debug("PROP = " + key + " OBJ  = " + obj);
				Class type = PropertyUtils.getPropertyType(obj, key);
				if(type.equals(List.class)){
					List value = (List) PropertyUtils.getProperty(obj, key);
					List<ItemAttribute> l = getItemAttributesForList(key ,value);
					attrs.addAll(l);
				}else{
					String s = getSDBRepresentation(key, obj);
					ItemAttribute ia = new ItemAttribute(key, s, true);
					attrs.add(ia);
				}
			}

		} catch (IllegalAccessException e) {
			log.error(e);
			throw new ApplicationException(e);
		} catch (InvocationTargetException e) {
			log.error(e);
			throw new ApplicationException(e);
		} catch (NoSuchMethodException e) {
			log.error(e);
			throw new ApplicationException(e);
		}

		return attrs;
	}
	
	protected List<ItemAttribute> getItemAttributesForList(String key , List value) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException{
		ArrayList<ItemAttribute> list = new ArrayList<ItemAttribute>();
		if(value == null){
			return list;
		}
		for(Object o : value){
			//log.info("Working on " + key + " VALUE =" + o);
			ItemAttribute ia = new ItemAttribute(key , getSDBRepresentation(key , o  , List.class) , true);
			list.add(ia);
		}
		return list;
	}

	
	protected String getSDBRepresentation(String name, Object value , Class type){
		if (name.equalsIgnoreCase("class"))
			return null; // i.e ignore class variable

		if (type.equals(java.util.Calendar.class)) {
			Calendar c = (Calendar) value;
			return c.getTime().getTime() + ""; // set Calandar as Date
		}
		if (type.equals(java.util.Date.class)) {
			Date dt = (Date) value;
			return dt.getTime() + ""; // set Calandar as Date
		}
		
		if (value != null)
			return value.toString();
		else
			return null;

	}
	
	protected String getSDBRepresentation(String name, Object bean)
			throws IllegalAccessException, InvocationTargetException,
			NoSuchMethodException {
		Class type = PropertyUtils.getPropertyType(bean, name);
		Object value = PropertyUtils.getProperty(bean, name);
		return getSDBRepresentation(name,value , type);
	}

	int index =0;
	protected Map<String, Object> getBeanUtilRepresentation(Class type,
			String value, String nm , ListBeanCounter counter) {
		HashMap<String, Object> ret = new HashMap<String, Object>();
		if (type.equals(List.class)) {
				counter.push(nm , value);
			
			/*
			String[] strs = value.split(",");
			ArrayList<String> l = new ArrayList<String>();
			for (int i = 0; i < strs.length; i++) {
				if (!strs[i].trim().equals("")) {
					l.add(strs[i]);
				}
				ret.put(nm, l);
				
			}*/
			
		} else if (type.equals(java.util.Calendar.class)) {
			String name = nm;
			Calendar c = Calendar.getInstance();
			c.setTimeInMillis(Long.parseLong(value));
			ret.put(name, c);
			//log.info("PUT  *** NAME = " + nm + " value=" + c.toString());
		}else if (type.equals(java.util.Date.class)) {
			String name = nm;
			Date dt = Calendar.getInstance().getTime();
			ret.put(name, dt);
			//log.info("PUT  *** NAME = " + nm + " value=" + c.toString());
		}
		else if (value != null && !value.trim().equals("")) {
			ret.put(nm, value);
		}else{
			index =0;
		}
		return ret;

	}

	
	public void mapItemAttrsToOject(String id , List<ItemAttribute> iaList,Object obj) {
		HashMap<String, Object> propMap = new HashMap<String, Object>();
		try {
			ListBeanCounter counter = new ListBeanCounter();
		for (ItemAttribute ia : iaList) {
			String name = ia.getName();
			String value = ia.getValue();
			Class type = PropertyUtils.getPropertyType(obj, name);
			 log.debug("Item ATTR = " + name + " VALUE=" + value + " Proerty Type=" + type + " Object=" + obj + " Name="+ name);
			propMap.putAll(getBeanUtilRepresentation(type, value, name , counter));
		}
		propMap.putAll(counter.flush());
		BeanUtils.populate(obj, propMap);
		} catch (IllegalAccessException e) {
			log.error("SDB ex", e);
			throw new ApplicationException(e);
		} catch (InvocationTargetException e) {
			log.error("SDB ex", e);
			throw new ApplicationException(e);
		} catch (NoSuchMethodException e) {
			log.error("SDB ex", e);
			throw new ApplicationException(e);
		}
	}
	
	
	public void mapItemToObject(Item item, Object obj) {
		HashMap<String, Object> propMap = new HashMap<String, Object>();
		try {
			
			List<ItemAttribute> ias = item.getAttributes();
			
			ListBeanCounter counter = new ListBeanCounter();
			for (ItemAttribute ia : ias) {
				String name = ia.getName();
				String value = ia.getValue();
				Class type = PropertyUtils.getPropertyType(obj, name);
				 log.debug("Item ATTR =" + name + " VALUE=" + value + " Proerty Type=" + type + " Object=" + obj + " Name="+ name);
				propMap.putAll(getBeanUtilRepresentation(type, value, name , counter));
			}
			propMap.putAll(counter.flush());
			
			BeanUtils.populate(obj, propMap);
		} catch (SDBException e) {
			log.error("SDB ex", e);
			throw new ApplicationException(e);
		} catch (IllegalAccessException e) {
			log.error("SDB ex", e);
			throw new ApplicationException(e);
		} catch (InvocationTargetException e) {
			log.error("SDB ex", e);
			throw new ApplicationException(e);
		} catch (NoSuchMethodException e) {
			log.error("SDB ex", e);
			throw new ApplicationException(e);
		}
		// log.debug("PROP MAP = " + propMap);
	}
	/**
	 * The Default Strategy is as follows.
	 * ENVNAME_CONICALCLASSNAME
	 * 
	 */
	
	public String getDomainName(Class clazz) {
		String className = clazz.getCanonicalName();
		String dname = className.replace('.', '_');
		
		String env = getEnvironment();
		if(env != null){
			return env + "_" + dname;
		}else
			return dname;
		
	}


	public Class getPersistedClazzOnDomainName(String name) {
		String cn = name.replace('_', '.');
		String env = getEnvironment();
		if(env != null){
			cn = cn.substring(env.length() + 1);
		}
		try {
			Class clazz = Class.forName(cn);
			return clazz;
		} catch (ClassNotFoundException e) {
			log.error("Domain Name Cannot map to a class", e);
			throw new ApplicationException(e);
		}
	}

	public String getEnvironment() {
		return environment;
	}

	public void setEnvironment(String environment) {
		this.environment = environment;
	}

	public class ListBeanCounter{
		Map<String, List<String>> items = new HashMap<String, List<String>>();

		public void push(String nm, String value) {
			List<String> l = items.get(nm);
			if(l == null){
				l = new ArrayList<String>();
				items.put(nm,l);
			}
			l.add(value);
		}
		
		public Map<String, List<String>> flush(){
			return items;
		}
	}
	

}
